<?php
/**
 * performance 향상 도구
 *
 * 가상화하여 해당 필드를 업데이트할 수 있도록 해줘서 퍼포먼스를 향상 시킴
 *
 * <code>
 * $utilRedisCount = utilRedisCount::getInstance('board_2');
 * $utilRedisCount->set(1);
 * </code>
 * @package     util
 * @subpackage     redis
 * @author 정상두
 *
 */
class utilRedisCount
{

    /**
     * utilRedis
     * @var object
     */
    private $utilRedis;

    /**
     * 브라우저 검사후 정상적이면 true 아니면 false;
     * @var boolean
     */
    private $bBrowser;

    private $sUserId;

    private $sTableName;

    private $iInterval;

    private $iLastExecuteTime;

    private $bTest;

    private static $instance;


    public static function getInstance($sUserId, $sTableName, $bTest=false)
    {
        if (!self::$instance) {
            self::$instance = new self($sUserId, $sTableName, $bTest);
        }
        return self::$instance;
    }

    public function __construct($sUserId, $sTableName, $bTest)
    {
        $this->sUserId           = $sUserId;
        $this->sTableName        = $sTableName;
        $this->bTest             = $bTest;


        $this->connect();
        $this->browser();
        $this->getInterval();

        $this->iLastExecuteTime  = (int) $this->utilRedis->get($this->sTableName.':last_execute_time');
    }

    private function connect()
    {
        $aRedisServer = $this->getRedisServerInfo();
        $this->utilRedis = utilRedis::predis($aRedisServer);
        //$this->utilRedis->select_db($aRedisServer['dbname']);
    }

    private function browser()
    {
        $utilBrowser     = new utilBrowser;
        $this->bBrowser  = $utilBrowser->Name == 'unknown' ? false : true;
    }

    private function getRedisServerInfo()
    {
        //WIN에서는 ssw001서버로 향하게 처리함. 무조건
        //근대 ssw001.cafe24test.com 서버는 머냐 소셜껀디
        $sRedisServer = strstr(PHP_OS, "WIN") ? '192.168.49.78' : 'localhost';


        if ($this->bTest) {
            $sRedisServer = '192.168.49.78';
        }

        return array(
            'host' => $sRedisServer,
            //'id' => 'ec_user',
            'database' => 'count_'.$this->sUserId,
            //'passwd' => '',
            'port' => '6379'
        );
    }

    public function getTableAllData()
    {
        $aKey = $this->utilRedis->keys($this->sTableName.':*:reg_time');
        $aData = array();

        foreach ($aKey as $sKey) {
            list ($sTableName, $iIndex, $sField) = explode(':', $sKey);

            $sCodeName = $sTableName.':'.$iIndex.':';
            $aData[] = array(
                'table_name'     => $sTableName,
                'iIndex'     => $iIndex,
                'count'         => $this->utilRedis->get($sCodeName.'count'),
                'reg_time'       => $this->utilRedis->get($sCodeName.'reg_time')
            );
        }
        return $aData;
    }

    /**
     * 2차원배열을 치환해 넘겨줍니다.
     *
     * rows로 받아온 데이터베이스의 값을 치환해 줍니다.
     *
     * <code>
     * <?
     * $aRows = modelMall::getModel()->getAll();
     * $this->rows($aRows, 'index key', 'view_count');
     * ?>
     * </code>
     *
     * @param unknown_type $aRows
     * @param unknown_type $iIndexKey       배열의 인덱스키
     * @param unknown_type $sReplaceField    치환될 키
     */

    public function rows($aRows, $iIndexKey, $sReplaceField)
    {
        foreach ($aRows as $iKey => $aRow) {
            $aRows[$iKey] = $this->row($aRow, $aRow[$iIndexKey], $sReplaceField);
        }
        return $aRows;
    }


    /**
     * 1차원 배열 치환
     *
     * 데이터베이스에서 받은 1차원 배열값을 치환해 줍니다.
     * <code>
     * <?
     * $aRow = modelMall::getModel()->get();
     * $this->row($aRow, 'index', 'view_count');
     * ?>
     * </code>
     * @param unknown_type $aRow
     * @param unknown_type $iIndex
     * @param unknown_type $sReplaceField
     */
    public function row($aRow, $iIndex, $sReplaceField)
    {
        $sCodeName = $this->sTableName.':'.$iIndex.':';

        if ($this->utilRedis->exists($sCodeName.'count')) {
            $iCount = $this->utilRedis->get($sCodeName.'count');
            $aRow[$sReplaceField] = $aRow[$sReplaceField]+$iCount;
        }
        return $aRow;
    }


    /**
     * 가상처리할 인덱스값
     *
     * 가상으로 처리할 레코드의 인덱스값을 입력하면 utilRedisCount는 가상데이터를 redis에 올린후
     * 관리하여 주며 일정한 시간이 지나면 배열값으로 리턴하여 줍니다.
     *
     * @param unknown_type $iIndex
     * @param unknown_type $iRegTime
     */
    public function set($iIndex, $iRegTime=0)
    {
        if (!$this->bBrowser) {
            return null;
        }


        $sCodeName = $this->sTableName.':'.$iIndex.':';
        $iRegTime  = $iRegTime == 0 ? time() : $iRegTime;

        $this->utilRedis->incr($sCodeName.'count');  //가감하고.
        $this->utilRedis->set($sCodeName.'reg_time', $iRegTime);

        $aUpdateList = array();

        //마지막 실행시간이 지났고 그 실행시간에
        if ($this->iLastExecuteTime < time()) {

            $this->setExecuteTime();
            $aKey = $this->utilRedis->keys($this->sTableName.':*:reg_time');

            $aUpdateList = $this->getUpdateKeys($aKey, $iKeyCount);

            $this->setInterval(sizeof($aUpdateList));

        }
        return $aUpdateList;
    }



    private function getUpdateKeys($aKey, $iKeyCount)
    {
        $iOverCount   = 20;
        $aUpdateList  = array();

        foreach ($aKey as $sKey) {

            list($sTableName, $iIndex, $sField) = explode(':', $sKey);
            $iRegTime = (int) $this->utilRedis->get($sKey);


            if ($iRegTime > (time()-60)) continue;


            $aUpdateList[] = array(
                'table'     => $sTableName,
                'index'     => $iIndex,
                'time'    => $iRegTime,
                'count'     => $this->utilRedis->get($sTableName.':'.$iIndex.':count')
            );

            $this->utilRedis->del($sTableName.':'.$iIndex.':count');
            $this->utilRedis->del($sTableName.':'.$iIndex.':reg_time');

            $iOverCount--;

            if ($iOverCount == 0) {
                 break;
            }
        }

        return $aUpdateList;
    }


    /**
     * 능동적으로 변하는 인터발 시간
     *
     * 전체레코드수가 개발자정의 수보다 넘으면 인터발 시간을 -5초하고 적으면 +시켜 사이트별로  특수하게 구동되게 만듬.
     * @param unknown_type $iKeyCount
     */
    private function setInterval($iUpdateKeyCount)
    {

        if ($iUpdateKeyCount > 19) {
            $iInterval = $this->iInterval - 3;
        } else {
            $iInterval = $this->iInterval + 3;
        }

        if ($iInterval < 2) {
            $iInterval = 2;
        } else if ($iInterval > 300) {
            $iInterval = 300;
        }

        $this->iInterval = $iInterval;

        return $this->utilRedis->set($this->sTableName.':interval', $iInterval);
    }

    /**
     * 인터발 시간 가져오기
     */
    public function getInterval()
    {
        $iInterval = (int) $this->utilRedis->get($this->sTableName.':interval');
        $this->iInterval = $iInterval == 0 ? 80 : $iInterval;
        return $this->iInterval;
    }




    /**
     * 마지막 실행시간 저장
     */
    public function setExecuteTime($iIntervalTime=0)
    {
        $this->iLastExecuteTime = ($iIntervalTime == 0) ? (time()+$this->iInterval) : $iIntervalTime;
        return $this->utilRedis->set($this->sTableName.':last_execute_time', $this->iLastExecuteTime);
    }



    public function flushRedisDb()
    {
        $this->utilRedis->flushdb();
    }


    public function __destruct()
    {
        $this->utilRedis->disconnect();
        unset($this->utilRedis);
    }
}